/**
 * Copyright 1996 self.com.au
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License. (http://www.apache.org/licenses/LICENSE-2.0)
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for 
 * the specific language governing permissions and limitations under the License.
 *
 * <dhamma-message src="Atisha (11th century Tibetan Buddhist master)">
 * 	The greatest achievement is selflessness. The greatest worth is self-mastery.
 * 	The greatest quality is seeking to serve others. The greatest precept is continual awareness.
 * 	The greatest medicine is the emptiness of everything. The greatest action is not conforming with the worlds ways.
 * 	The greatest magic is transmuting the passions. The greatest generosity is non-attachment.
 * 	The greatest goodness is a peaceful mind. The greatest patience is humility.
 * 	The greatest effort is not concerned with results. The greatest meditation is a mind that lets go.
 * 	The greatest wisdom is seeing through appearances. 
 * </dhamma-message>
 *
 * @author Ashin Wimalajeewa
 *
 * Note, this is a very old runtime library that I have evolved since 1996. I rarely use it
 * now because the Apache Commons project has made it obsolete.
 */
/*****************************************************************************
 * Copyright (C) The Apache Software Foundation. All rights reserved.        *
 * ------------------------------------------------------------------------- *
 * This software is published under the terms of the Apache Software License *
 * version 1.1, a copy of which has been included with this distribution in  *
 * the LICENSE file.                                                         *
 *****************************************************************************/

package self.xml;

import java.awt.geom.PathIterator;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;

import org.apache.batik.bridge.SVGUtilities;
import org.apache.batik.transcoder.SVGAbstractTranscoder;
import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderOutput;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.svg.SVGAElement;
import org.w3c.dom.svg.SVGElement;
import org.w3c.dom.traversal.DocumentTraversal;
import org.w3c.dom.traversal.NodeFilter;
import org.w3c.dom.traversal.NodeIterator;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

/**
 * This class transcodes an input to a html imagemap.
 *
 * @author <a href="mailto:[EMAIL PROTECTED]";>Torsten Knodt</a>
 */
public class ImageMapTranscoder extends SVGAbstractTranscoder {

    private final String XHTML_URI = "http://www.w3.org/1999/xhtml";
    private final String CDATA = "CDATA";
    private final String AREA_TAG = "area";
    private final String MAP_TAG = "map";
    private final String TITLE_ATTR = "title";
    private final String ALT_ATTR = "alt";
    private final String AREA_TAG_SHAPE_ATTR = "shape";
    private final String AREA_TAG_SHAPE_ATTR_POLY = "poly";
    private final String AREA_TAG_COORDS_ATTR = "coords";
    private final String AREA_TAG_HREF_ATTR = "href";
    //private final String XHTML_SYSID = "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";
    //private final String XHTML_PUBID = "-//W3C//DTD XHTML 1.0 Strict//EN";

    /**
     * Constructs a new <tt>ImageMapTranscoder</tt>.
     */
    public ImageMapTranscoder() {
    	// nothing to do
    }

    /**
     * Get SAX Transformer factory
     *
     * @result SAX Transformer factory to use
     */
    private SAXTransformerFactory getTransformerHandlerFactory()
    {
        return (SAXTransformerFactory) SAXTransformerFactory.newInstance();
    }

    /**
     * Get SAX Transformer from Factory
     *
     * @param output the output where to transcode
     * @result the transformer handler
     */
    private TransformerHandler getTransformerHandler(TranscoderOutput output)
    throws TransformerConfigurationException
    {
        SAXTransformerFactory tfactory = getTransformerHandlerFactory();
        if (tfactory.getFeature(SAXSource.FEATURE) && tfactory.getFeature(StreamResult.FEATURE))
        {
            TransformerHandler ch = tfactory.newTransformerHandler();
            ch.setResult (new StreamResult (output.getOutputStream()));
            return ch;
        }
        return null;
    }

    /**
     * Configure the SAX transformer handler
     *
     * @param th the SAX transformer handler
     */
    private void configureTransformerHandler(TransformerHandler th)
    {
        //        th.setSystemId (XHTML_SYSID);
        // FIXME: ID/ name attribute must be added to map element to make output valid
    }

    /**
     * Configure the SAX transformer
     *
     * @param th the SAX transformer
     */
    private void configureTransformer(Transformer t)
    {
        //        t.setOutputProperty (OutputKeys.DOCTYPE_PUBLIC, XHTML_PUBID);
        //        t.setOutputProperty (OutputKeys.DOCTYPE_SYSTEM, XHTML_SYSID);
        // FIXME: ID/ name attribute must be added to map element to make output valid
        t.setOutputProperty (OutputKeys.INDENT, "yes");
        t.setOutputProperty (OutputKeys.MEDIA_TYPE, "text/xml");
        t.setOutputProperty (OutputKeys.STANDALONE, "no");
        t.setOutputProperty (OutputKeys.OMIT_XML_DECLARATION, "no");
        t.setOutputProperty (OutputKeys.METHOD, "xml");
    }

    /**
     * Sets the title attribute for an element
     *
     * @param element the element to fetch the title for
     * @param att the Attribute Implementation to use
     */
    private void setTitle(SVGElement element, AttributesImpl att)
    {
        // We could also use xlink:title, but I think svg:desc is better.
        String s = SVGUtilities.getDescription (element);
        if (s != null)
            if (! s.equals (""))
                att.addAttribute("", TITLE_ATTR, TITLE_ATTR, CDATA, s);
    }

    /**
     * Sets the alt attribute for an element
     *
     * @param element the element to fetch the title for
     * @param att the Attribute Implementation to use
     */
    private void setAlt(SVGElement element, AttributesImpl att)
    {
        // Here svg:title should be used instead of svg:desc, I think. Perhaps xlink:title would also be an idea.
        String s = SVGUtilities.getDescription (element);
        System.out.println (s);
        if (s == null)
            s = "";
        att.addAttribute("", ALT_ATTR, ALT_ATTR, CDATA, s);
    }

    /**
     * Transcodes the specified ResourceAttributes as an image in the specified output.
     *
     * @param document the document to transcode
     * @param uri the uri of the document or null if any
     * @param output the ouput where to transcode
     * @exception TranscoderException if an error occured while transcoding
     */
    protected void transcode(Document document,
                             String uri,
                             TranscoderOutput output)
    throws TranscoderException
    {

        super.transcode(document, uri, output);

        TransformerHandler filter;
        try {
            filter = getTransformerHandler(output);
        } catch (TransformerConfigurationException ex)
        {
            throw new TranscoderException (ex);
        }
        configureTransformerHandler (filter);
        configureTransformer (filter.getTransformer());
        AttributesImpl myatt = new AttributesImpl();
        setTitle ((SVGElement) (document.getDocumentElement()), myatt);
        try {
            filter.startDocument();
            //            filter.startDTD(MAP_TAG, XHTML_PUBID, XHTML_SYSID);
            //            filter.endDTD();
            // FIXME: ID/ name attribute must be added to map element to make output valid
            filter.startElement (XHTML_URI, MAP_TAG, MAP_TAG, myatt);
        } catch (SAXException ex) { throw new TranscoderException (ex); }
        NodeIterator i = ((DocumentTraversal) document).createNodeIterator(document, NodeFilter.SHOW_ELEMENT, null, true);
        Node node;
        while ((node = i.nextNode()) != null)
        {
            SVGAElement e;
            try {
                e = (SVGAElement) node;
            } catch (ClassCastException ex) { e = null; }

            if (e != null)
            {
                String coords = "";
                int[] lastpoints = null;
                for (PathIterator path = builder.build (ctx,e).getOutline().getPathIterator(curTxf, 1.0); ! path.isDone(); path.next())
                {
                    float[] points = new float[6];
                    int seg = path.currentSegment (points);
                    int[] newpoints = { Math.round(points[0]), Math.round(points[1]) };
                    switch (seg)
                    {
                    case PathIterator.SEG_CLOSE:
                        // End of path segment, so push out Tag
                        if (! coords.equals(""))
                        {
                            AttributesImpl att = new AttributesImpl();
                            att.addAttribute("", AREA_TAG_SHAPE_ATTR, AREA_TAG_SHAPE_ATTR, CDATA, AREA_TAG_SHAPE_ATTR_POLY);
                            att.addAttribute("", AREA_TAG_COORDS_ATTR, AREA_TAG_COORDS_ATTR, CDATA, coords);
                            att.addAttribute("", AREA_TAG_HREF_ATTR, AREA_TAG_HREF_ATTR, CDATA, e.getHref().getBaseVal());
                            setTitle (e, att);
                            setAlt (e, att);
                            try {
                                filter.startElement(XHTML_URI, AREA_TAG, AREA_TAG, att);
                                filter.endElement(XHTML_URI, AREA_TAG, AREA_TAG);
                            } catch (SAXException ex) { throw new TranscoderException (ex); }
                            coords = "";
                            lastpoints = null;
                        }
                        break;
                    case PathIterator.SEG_CUBICTO:
                        // Cubic segment not allowed in flattened path
                        throw new TranscoderException ("Cubic segment not allowed in flattened path");
                    case PathIterator.SEG_QUADTO:
                        // Quad segment not allowed in flattened path
                        throw new TranscoderException ("Quad segment not allowed in flattened path");
                    case PathIterator.SEG_LINETO:
                        if (coords.equals(""))
                            throw new TranscoderException ("Line segment not allowed at begin of path");
                        // Add line segment to polygon coords
                        if (! lastpoints.equals (newpoints))
                            coords = coords + "," + newpoints[0] + "," + newpoints[1];
                        break;
                    case PathIterator.SEG_MOVETO:
                        if (! coords.equals(""))
                            throw new TranscoderException ("Move segment only allowed at begin of path");
                        // Use move segment as polygon start coords
                        coords = newpoints[0] + "," + newpoints[1];
                        break;
                    default:
                        throw new TranscoderException ("Unknown segment with type number " + seg);
                    }
                    lastpoints = newpoints;
                }
            }
        }
        try {
            filter.endElement (XHTML_URI, MAP_TAG, MAP_TAG);
            filter.endDocument();
        } catch (SAXException ex) { throw new TranscoderException (ex); }
    }

}